﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net
{
    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Text;

    /// <summary>Provides <b>generic</b> means to convert a string into a <typeparamref name="TParsable"/> object.
    /// </summary>
    /// <typeparam name="TParsable">The type that is parsed from a string. This must either offer a public static method
    /// called "Parse" accepting a single <see cref="string"/> parameter or be the <see cref="string"/> type itself.
    /// </typeparam>
    internal static class Parser<TParsable>
    {
        private static readonly Func<string, TParsable> ParseMethod = GetParseMethod();
        private static readonly Func<ArraySegment<byte>, Encoding, TParsable> RawParseMethod = GetRawParseMethod();

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static TParsable Parse(string s)
        {
            return ParseMethod(s);
        }

        internal static TParsable Parse(ArraySegment<byte> segment, Encoding encoding)
        {
            return RawParseMethod(segment, encoding);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private static Func<string, TParsable> GetParseMethod()
        {
            MethodInfo method = typeof(string) == typeof(TParsable) ?
                ParserHelper.GetParseMethod<ParserHelper>("ParseString", true) :
                ParserHelper.GetParseMethod<TParsable>("Parse", false);

            if (method == null)
            {
                return s =>
                {
                    throw new InvalidOperationException(
                        typeof(TParsable).FullName + " does not offer a public static Parse method.");
                };
            }
            else
            {
                return (Func<string, TParsable>)
                    Delegate.CreateDelegate(typeof(Func<string, TParsable>), null, method);
            }
        }

        private static Func<ArraySegment<byte>, Encoding, TParsable> GetRawParseMethod()
        {
            MethodInfo rawMethodInfo;

            if (typeof(string) == typeof(TParsable))
            {
                rawMethodInfo = ParserHelper.GetRawParseMethod<ParserHelper>("RawParseString");
            }
            else
            {
                var methodInfo = ParserHelper.GetParseMethod<TParsable>("Parse", false);

                if (methodInfo == null)
                {
                    return (s, e) =>
                        {
                            throw new InvalidOperationException(
                                typeof(TParsable).FullName + " does not offer a public static Parse method.");
                        };
                }
                else
                {
                    var method = (Func<string, TParsable>)Delegate.CreateDelegate(
                        typeof(Func<string, TParsable>), null, methodInfo);
                    return (s, e) => method(e.GetString(s.Array, s.Offset, s.Count));
                }
            }

            return (Func<ArraySegment<byte>, Encoding, TParsable>)
                Delegate.CreateDelegate(typeof(Func<ArraySegment<byte>, Encoding, TParsable>), null, rawMethodInfo);
        }
    }
}
